﻿#include "window.h"
#include "mainwindow.h"
#include "oglwidget.h"
#include "qtabledialog.h"
#include "qsmoothdialog.h"

#include <QSlider>
#include <QLabel>
#include <QLineEdit>
#include <QSpinBox>
#include <QDoubleSpinBox>
#include <QCheckBox>
#include <QTextBrowser>
#include <QComboBox>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QGridLayout>
#include <QPushButton>
#include <QDesktopWidget>
#include <QApplication>
#include <QMessageBox>
#include <QDebug>
#include <QTableWidget>
#include <QGroupBox>

bool Window::measurementCheckable = true;

Window::Window(MainWindow *parent,bool nf, bool of):
    mainWindow(parent),newFirst(nf),openFirst(of)
{ 
    setWindowTitle(tr("ObjViewer"));
    setPalette(QPalette(Qt::white));    //set window bg color
    setAttribute(Qt::WA_DeleteOnClose);
    setFocusPolicy(Qt::StrongFocus);    //accept keyboard input
    setFocus();

    createComponents();
    iniUI();

    iniSignalSlots();

    _rangeAngle.resize(_range);
    _rangeEdge.resize(_range);
    _rangeAreaEdge.resize(_range);
    _rangeInOutRadius.resize(_range);
    _rangeInRadiusMaxEdge.resize(_range);
    _rangeMinHeightMaxEdge.resize(_range);
    _rangeQ.resize(_range);
    _rangeQ2.resize(_range);
}

Window::~Window(){}

/*
 * private functions
 */

void Window::createComponents()
{
    oglWidget = new OglWidget(this,newFirst,openFirst);
    speedSlider = createSlider();

    modelBasicGroupBox = createGroupBox("Default");
    vnLineEdit = createLineEdit();
    fnLineEdit = createLineEdit();

    modelMeasurementGroupBox = createGroupBox("Measurements");
    somaSurfaceLineEdit = createLineEdit();
    totalSurfaceLineEdit = createLineEdit();
    totalVolumeLineEdit = createLineEdit();

    estimationComboBox = new QComboBox;
    estimationComboBox->addItem("MinAngle");
    estimationComboBox->addItem("MaxMinEdge");
    estimationComboBox->addItem("AreaEdge");
    estimationComboBox->addItem("OutInRadius");
    estimationComboBox->addItem("InRadiusMaxEdge");
    estimationComboBox->addItem("MinHeightMaxEdge");
    estimationComboBox->addItem(tr("Q"));
    estimationComboBox->addItem("Q2");
    estimationComboBox->addItem("Total");

    neuronGroupBox = createGroupBox("Neuron Attributes");
    neuronGroupBox->setEnabled(false);
    neuronInfo = createPushButton("Info");
    neuronSomaFix = createPushButton("SF");
    neuronSomaAuto = createPushButton("SA");
    neuronColor = createPushButton("Colored");
    neuronGrow1 = createPushButton("Grow1");
    neuronGrow2 = createPushButton("Grow2");
    neuronSpin1 = createDSpinBox(1.0);
    neuronSpin2 = createDSpinBox(0.5);
    neuronSpin3 = createSpinBox(0);
    neuronSpin3->setRange(1,1000);
    neuronSpin3->setSingleStep(20);
    neuronSpin4 = createSpinBox(3);
    neuronSpin4->setRange(1,15);
    neuronSpin4->setSingleStep(1);

    infoGroupBox = createGroupBox("Info Panel");
    infoTextBrowser = new QTextBrowser;

    dockBtn = new QPushButton(tr("Undock"), this);
}

void Window::iniUI()
{
    QHBoxLayout *container = new QHBoxLayout;
    container->addWidget(oglWidget,Qt::AlignCenter);
    container->addWidget(speedSlider);

    QVBoxLayout *right = new QVBoxLayout;
    container->addLayout(right);

    QVBoxLayout *mainLayout = new QVBoxLayout;
    mainLayout->addLayout(container);
    mainLayout->addWidget(dockBtn);
    this->setLayout(mainLayout);

    //设置right布局内容
    QHBoxLayout *layout1 = new QHBoxLayout();
    layout1->addWidget(createLabel("VN"));
    layout1->addWidget(vnLineEdit);
    layout1->addWidget(createLabel("FN"));
    layout1->addWidget(fnLineEdit);
    modelBasicGroupBox->setLayout(layout1);
    right->addWidget(modelBasicGroupBox);

    QVBoxLayout *layout2 = new QVBoxLayout;

    QHBoxLayout *line1 = new QHBoxLayout;
    line1->addWidget(createLabel("Soma Surface(um<sup>2</sup>)",Qt::AlignLeft));
    line1->addWidget(somaSurfaceLineEdit);
    layout2->addLayout(line1);

    QHBoxLayout *line2 = new QHBoxLayout;
    line2->addWidget(createLabel("Total Surface(um<sup>2</sup>)",Qt::AlignLeft));
    line2->addWidget(totalSurfaceLineEdit);
    layout2->addLayout(line2);

    QHBoxLayout *line3 = new QHBoxLayout;
    line3->addWidget(createLabel("Total Volume(um<sup>3</sup>)",Qt::AlignLeft));
    line3->addWidget(totalVolumeLineEdit);
    layout2->addLayout(line3);

    QHBoxLayout *line4 = new QHBoxLayout;
    line4->addWidget(createLabel("Estimation",Qt::AlignLeft));
    line4->addWidget(estimationComboBox);
    layout2->addLayout(line4);

    modelMeasurementGroupBox->setLayout(layout2);
    right->addWidget(modelMeasurementGroupBox);

    QVBoxLayout *layoutNeuronGroup = new QVBoxLayout;
    QHBoxLayout *layout3 = new QHBoxLayout;
    layout3->addWidget(neuronInfo);
    layout3->addWidget(neuronSomaFix);
    layout3->addWidget(neuronSomaAuto);
    layoutNeuronGroup->addLayout(layout3);
    QHBoxLayout *layout4 = new QHBoxLayout;
    layout4->addWidget(neuronColor);
    layout4->addWidget(neuronGrow1);
    layout4->addWidget(neuronGrow2);
    layoutNeuronGroup->addLayout(layout4);

    QHBoxLayout *layout5 = new QHBoxLayout;
    layout5->addWidget(createLabel("Transparency"));
    layout5->addWidget(neuronSpin1);
    layout5->addWidget(neuronSpin2);
    layoutNeuronGroup->addLayout(layout5);

    QHBoxLayout *layout6 = new QHBoxLayout;
    layout6->addWidget(createLabel("SomaCircle"));
    layout6->addWidget(neuronSpin3);
    layout6->addWidget(neuronSpin4);
    layoutNeuronGroup->addLayout(layout6);

    neuronGroupBox->setLayout(layoutNeuronGroup);
    right->addWidget(neuronGroupBox);

    QVBoxLayout *layout7 = new QVBoxLayout;
    layout7->addWidget(infoTextBrowser);
    infoGroupBox->setLayout(layout7);

    right->addWidget(infoGroupBox);
}

void Window::iniSignalSlots()
{
    connect(oglWidget, &OglWidget::setVN, vnLineEdit, &QLineEdit::setText);
    connect(oglWidget, &OglWidget::setFN, fnLineEdit, &QLineEdit::setText);
    connect(oglWidget,&OglWidget::_setInfoPanel,this->infoTextBrowser,&QTextBrowser::insertPlainText);
    connect(oglWidget,&OglWidget::enableNeuronGroup,this->neuronGroupBox,&QGroupBox::setEnabled);
    connect(oglWidget,&OglWidget::enableNeuronGroup,this->neuronSpin1,&QSpinBox::setEnabled);

    connect(mainWindow, &MainWindow::selectedFile, oglWidget, &OglWidget::_loadObject);
    connect(mainWindow, &MainWindow::render_Mode, oglWidget, &OglWidget::setMenuMode);
    connect(mainWindow,&MainWindow::selectedFile,this,&Window::_setModelState);

    connect(this->speedSlider,&QSlider::valueChanged,oglWidget,&OglWidget::_setInteractorSpeed);
    connect(this->estimationComboBox,static_cast<void(QComboBox::*)(int)>(&QComboBox::activated),this,&Window::_receiveEstimationCombo);
    connect(this->neuronInfo,&QPushButton::clicked,oglWidget,&OglWidget::_setInfos);
    connect(this->neuronColor,&QPushButton::clicked,this,&Window::on_btnNeuronColor_clicked);
    connect(this->neuronGrow1,&QPushButton::clicked,this,&Window::on_btnNeuronGrow1_clicked);
    connect(this->neuronGrow2,&QPushButton::clicked,this,&Window::on_btnNeuronGrow2_clicked);
    connect(this->neuronSomaFix,&QPushButton::clicked,this,&Window::on_btnNeuronSF_clicked);
    connect(this->neuronSomaAuto,&QPushButton::clicked,this,&Window::on_btnNeuronSA_clicked);
    connect(this->neuronSpin1,static_cast<void(QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged),oglWidget,&OglWidget::_setAlpha1);
    connect(this->neuronSpin2,static_cast<void(QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged),oglWidget,&OglWidget::_setAlpha2);
    connect(this->neuronSpin3,static_cast<void(QSpinBox::*)(int)>(&QSpinBox::valueChanged),oglWidget,&OglWidget::_setCircleOutter);
    connect(this->neuronSpin4,static_cast<void(QSpinBox::*)(int)>(&QSpinBox::valueChanged),oglWidget,&OglWidget::_setCircleInner);
    connect(this,&Window::_setInfoPanel,this->infoTextBrowser,&QTextBrowser::insertPlainText);
    connect(this->dockBtn, &QPushButton::clicked, this, &Window::dockUndock);
}

/*
 * slots
 */
void Window::dockUndock()
{
    if (parent())
    {
        setParent(0);
        setAttribute(Qt::WA_DeleteOnClose);
        move(QApplication::desktop()->width()/2 - width()/2, QApplication::desktop()->height()/2 - height()/2);
        dockBtn->setText(tr("Dock"));

        show();

        mainWindow->_screenTips = true; //set mainWindow's centralWidget
        mainWindow->_setScreenHints();
    }
    else
    {
        if(!mainWindow->isVisible()){
            QMessageBox::information(0, tr("Cannot dock"), tr("Main window already closed"));
            return;
        }

        if(mainWindow->_screenTips == true){
            mainWindow->_screenTips = false;
            mainWindow->centralWidget()->close();
            mainWindow->takeCentralWidget()->deleteLater();

            setAttribute(Qt::WA_DeleteOnClose, false);
            dockBtn->setText(tr("Undock"));
            mainWindow->setCentralWidget(this);
        }
        else{
            if (!mainWindow->centralWidget())
            {
                setAttribute(Qt::WA_DeleteOnClose, false);
                dockBtn->setText(tr("Undock"));
                mainWindow->setCentralWidget(this);
            } else {
                QMessageBox::information(0, tr("Cannot dock"), tr("Main window already occupied"));
            }
        }
    }
}

//set model state in UI
void Window::_setModelState(QString &str){
    QStringList list = str.split('/');
    modelBasicGroupBox->setTitle(*list.rbegin());

    _initWindowState();

    _computeMeasurements();
}

void Window::_initWindowState(){
    somaSurfaceLineEdit->clear();
    totalSurfaceLineEdit->clear();
    totalVolumeLineEdit->clear();

    if(minAngleTbl != nullptr){
        delete minAngleTbl;
        minAngleTbl = nullptr;
        _rangeAngle.clear();
        _rangeAngle.resize(_range);
    }

    if(maxMinEdgeTbl!=nullptr){
        delete maxMinEdgeTbl;
        maxMinEdgeTbl = nullptr;
        _rangeEdge.clear();
        _rangeEdge.resize(_range);
    }

    if(areaEdgeTbl!=nullptr){
        delete areaEdgeTbl;
        areaEdgeTbl = nullptr;
        _rangeAreaEdge.clear();
        _rangeAreaEdge.resize(_range);
    }

    if(outInRadiusTbl!=nullptr){
        delete outInRadiusTbl;
        outInRadiusTbl = nullptr;
        _rangeInOutRadius.clear();
        _rangeInOutRadius.resize(_range);
    }

    if(inRadiusMaxEdgeTbl!=nullptr){
        delete inRadiusMaxEdgeTbl;
        inRadiusMaxEdgeTbl = nullptr;
        _rangeInRadiusMaxEdge.clear();
        _rangeInRadiusMaxEdge.resize(_range);
    }

    if(minHeightMaxEdgeTbl!=nullptr){
        delete minHeightMaxEdgeTbl;
        minHeightMaxEdgeTbl = nullptr;
        _rangeMinHeightMaxEdge.clear();
        _rangeMinHeightMaxEdge.resize(_range);
    }

    if(qTbl!=nullptr){
        delete qTbl;
        qTbl = nullptr;
        _rangeQ.clear();
        _rangeQ.resize(_range);
    }

    if(q2Tbl!=nullptr){
        delete q2Tbl;
        q2Tbl = nullptr;
        _rangeQ2.clear();
        _rangeQ2.resize(_range);
    }

    if(totalTbl!=nullptr){
        delete totalTbl;
        totalTbl = nullptr;
    }

}

void Window::_computeMeasurements(){
    float somaSurface = 0.0f;
    float totalSurface = 0.0f;
    float totalVolume = 0.0f;
    auto f = oglWidget->_vcgmesh.face.begin();
    while(f!=oglWidget->_vcgmesh.face.end()){
       f->computeTriangleArea();
       if(f->IsS()){
           if(oglWidget->_vcgmesh.markVert[f->V(0)]!=1&&oglWidget->_vcgmesh.markVert[f->V(1)]!=1&&oglWidget->_vcgmesh.markVert[f->V(2)]!=1)
               somaSurface += f->getArea();
       }
       totalSurface += f->getArea();
       totalVolume += f->getVolume();
       f++;
    }
    totalVolume/=6;

    somaSurfaceLineEdit->setText(QString::number(somaSurface));
    totalSurfaceLineEdit->setText(QString::number(totalSurface));
    totalVolumeLineEdit->setText(QString::number(glm::abs(totalVolume)));
}

//estimation value
void Window::_receiveEstimationCombo(int combo){
    switch (combo) {
    case 0:
    {
        //最小夹角
        emit _setInfoPanel("MinAngle\n");

        if(minAngleTbl==nullptr){
            _computeMinAngle();
            minAngleTbl = new QTableDialog(this);
            minAngleTbl->setWindowTitle("MinAngle");
            minAngleTbl->createItemsARow(0,_rangeAngle);
        }
        minAngleTbl->show();
        break;
    }

    case 1:
    {
        //最大最小边
        emit _setInfoPanel("MaxMinEdge\n");

        if(maxMinEdgeTbl==nullptr){
            _computeMaxMinEdge();
            maxMinEdgeTbl = new QTableDialog(this);
            maxMinEdgeTbl->setWindowTitle("MaxMinEdge");
            maxMinEdgeTbl->createItemsARow(0,_rangeEdge);
        }
        maxMinEdgeTbl->show();
        break;
    }

    case 2:
    {
        //面积-边长
        emit _setInfoPanel("AreaEdge\n");

        if(areaEdgeTbl==nullptr){
            _computeAreaEdge();
            areaEdgeTbl = new QTableDialog(this);
            areaEdgeTbl->setWindowTitle("AreaEdge");
            areaEdgeTbl->createItemsARow(0,_rangeAreaEdge);
        }
        areaEdgeTbl->show();
        break;
    }

    case 3:
    {
         //内外半径
        emit _setInfoPanel("OutInRadius\n");

        if(outInRadiusTbl==nullptr){
            _computeInOutRadius();
            outInRadiusTbl = new QTableDialog(this);
            outInRadiusTbl->setWindowTitle("OutInRadius");
            outInRadiusTbl->createItemsARow(0,_rangeInOutRadius);
        }
        outInRadiusTbl->show();
        break;
    }

    case 4:
    {
        //内半径-最大边
        emit _setInfoPanel("InRadiusMaxEdge\n");

        if(inRadiusMaxEdgeTbl==nullptr){
            _computeInRadiusMaxEdge();
            inRadiusMaxEdgeTbl = new QTableDialog(this);
            inRadiusMaxEdgeTbl->setWindowTitle("InRadiusMaxEdge");
            inRadiusMaxEdgeTbl->createItemsARow(0,_rangeInRadiusMaxEdge);
        }
        inRadiusMaxEdgeTbl->show();
        break;
    }

    case 5:
    {
        //最小高-最大边
        emit _setInfoPanel("MinHeightMaxEdge\n");

        if(minHeightMaxEdgeTbl==nullptr){
            _computeMinHeightMaxEdge();
            minHeightMaxEdgeTbl = new QTableDialog(this);
            minHeightMaxEdgeTbl->setWindowTitle("MinHeightMaxEdge");
            minHeightMaxEdgeTbl->createItemsARow(0,_rangeMinHeightMaxEdge);
        }
        minHeightMaxEdgeTbl->show();
        break;
    }

    case 6:
    {
        //Li and Yuan: Q
        emit _setInfoPanel("Q\n");

        if(qTbl==nullptr){
            _computeQ();
            qTbl = new QTableDialog(this);
            qTbl->setWindowTitle("Q");
            qTbl->createItemsARow(0,_rangeQ);
        }
        qTbl->show();
        break;
    }

    case 7:
    {
        emit _setInfoPanel("Q2\n");

        if(q2Tbl==nullptr){
            _computeQ2();
            q2Tbl = new QTableDialog(this);
            q2Tbl->setWindowTitle("Q2");
            q2Tbl->createItemsARow(0,_rangeQ2);
        }
        q2Tbl->show();
        break;
    }

    case 8:
    {
        if(!minAngleTbl
                &&!maxMinEdgeTbl
                &&!areaEdgeTbl
                &&!outInRadiusTbl
                &&!inRadiusMaxEdgeTbl
                &&!minHeightMaxEdgeTbl
                &&!qTbl
                &&!q2Tbl){
            _setInfoPanel("Pleaase compute firstly\n");
            break;
        }

        emit _setInfoPanel("Total Estimation\n");
        if(totalTbl == nullptr){
            totalTbl = new QTableDialog(this,true);
            totalTbl->setWindowTitle("Total");
        }

        //插入数据
        if(minAngleTbl&&((bitFlag&(1<<0))==0)){
            bitFlag=bitFlag|(1<<0);
            totalTbl->insertItemsARow(minAngleTbl,QString("minAngle"));
        }
        if(maxMinEdgeTbl&&((bitFlag&(1<<1))==0)){
            bitFlag|=(1<<1);
            totalTbl->insertItemsARow(maxMinEdgeTbl,QString("maxMinEdge"));
        }
        if(areaEdgeTbl&&((bitFlag&(1<<2))==0)){
            bitFlag|=(1<<2);
            totalTbl->insertItemsARow(areaEdgeTbl,QString("areEdge"));
        }
        if(outInRadiusTbl&&((bitFlag&(1<<3))==0)){
            bitFlag|=(1<<3);
            totalTbl->insertItemsARow(outInRadiusTbl,QString("outInRadius"));
        }
        if(inRadiusMaxEdgeTbl&&((bitFlag&(1<<4))==0)){
            bitFlag|=(1<<4);
            totalTbl->insertItemsARow(inRadiusMaxEdgeTbl,QString("inRadiusMaxEdge"));
        }
        if(minHeightMaxEdgeTbl&&((bitFlag&(1<<5))==0)){
            bitFlag|=(1<<5);
            totalTbl->insertItemsARow(minHeightMaxEdgeTbl,QString("minHeightMaxEdge"));
        }
        if(qTbl&&((bitFlag&(1<<6))==0)){
            bitFlag|=(1<<6);
            totalTbl->insertItemsARow(qTbl,QString("Q"));
        }
        if(q2Tbl&&((bitFlag&(1<<7))==0)){
            bitFlag|=(1<<7);
            totalTbl->insertItemsARow(q2Tbl,QString("Q2"));
        }

        totalTbl->show();
        break;
    }

    default:
        break;
    }
}

void Window::_computeMinAngle(){
    auto f = oglWidget->_vcgmesh.face.begin();

    while(f!=oglWidget->_vcgmesh.face.end()){
        glm::vec3 v1(f->P(0).X(), f->P(0).Y(), f->P(0).Z());
        glm::vec3 v2(f->P(1).X(), f->P(1).Y(), f->P(1).Z());
        glm::vec3 v3(f->P(2).X(), f->P(2).Y(), f->P(2).Z());

        float a1 = _computeVectorAngle(v2-v1,v3-v1);
        float a2 = _computeVectorAngle(v1-v2,v3-v2);
        float a3 = _computeVectorAngle(v1-v3,v2-v3);
        float aMin = maxValue<float>(a1,a2,a3);
        float q_aMin = 3.0f*aMin/180.0f;

        setRange(_rangeAngle,q_aMin);

        f++;
    }
    for(auto &r:_rangeAngle){
        r.second = 100.0f*r.first/oglWidget->_vcgmesh.fn;
        r.second = QString::number(r.second,'f',5).toFloat();
    }
}

void Window::_computeMaxMinEdge(){
    auto f = oglWidget->_vcgmesh.face.begin();
    while(f!=oglWidget->_vcgmesh.face.end()){
        glm::vec3 v1(f->P(0).X(), f->P(0).Y(), f->P(0).Z());
        glm::vec3 v2(f->P(1).X(), f->P(1).Y(), f->P(1).Z());
        glm::vec3 v3(f->P(2).X(), f->P(2).Y(), f->P(2).Z());

        float len1 = glm::distance(v1, v2);
        float len2 = glm::distance(v2, v3);
        float len3 = glm::distance(v1, v3);
        float lenMin = maxValue<float>(len1, len2, len3);
        float lenMax = maxValue<float>(len1, len2, len3, true);

        float q_LI = lenMin / lenMax;
        setRange(_rangeEdge, q_LI);

        f++;
    }
    for(auto &r:_rangeEdge){
        r.second = 100.0f*r.first/oglWidget->_vcgmesh.fn;
        r.second = QString::number(r.second,'f',5).toFloat();
    }
}

void Window::_computeAreaEdge(){
    auto f = oglWidget->_vcgmesh.face.begin();
    while(f!=oglWidget->_vcgmesh.face.end()){
        glm::vec3 v1(f->P(0).X(), f->P(0).Y(), f->P(0).Z());
        glm::vec3 v2(f->P(1).X(), f->P(1).Y(), f->P(1).Z());
        glm::vec3 v3(f->P(2).X(), f->P(2).Y(), f->P(2).Z());
        auto v1v2 = v2 - v1;
        auto v2v3 = v3 - v2;
        auto v1v3 = v3 - v1;
        float len1_2 = v1v2.x*v1v2.x + v1v2.y*v1v2.y + v1v2.z*v1v2.z;
        float len2_2 = v2v3.x*v2v3.x + v2v3.y*v2v3.y + v2v3.z*v2v3.z;
        float len3_2 = v1v3.x*v1v3.x + v1v3.y*v1v3.y + v1v3.z*v1v3.z;

        if(measurementCheckable){
            f->computeTriangleArea();
        }
        float A = f->getArea();
        float q_ALS = 4.0f*sqrtf(3.0f)*A/(len1_2+len2_2+len3_2);
        setRange(_rangeAreaEdge,q_ALS);
        f++;
    }

    for(auto &r:_rangeAreaEdge){
        r.second = 100.0f*r.first/oglWidget->_vcgmesh.fn;
        r.second = QString::number(r.second,'f',5).toFloat();
    }
}

void Window::_computeInOutRadius(){
    auto f = oglWidget->_vcgmesh.face.begin();
    while(f!=oglWidget->_vcgmesh.face.end()){
        glm::vec3 v1(f->P(0).X(), f->P(0).Y(), f->P(0).Z());
        glm::vec3 v2(f->P(1).X(), f->P(1).Y(), f->P(1).Z());
        glm::vec3 v3(f->P(2).X(), f->P(2).Y(), f->P(2).Z());

        if(measurementCheckable){
            f->computeTriangleArea();
        }
        float A = f->getArea();
        float a = glm::distance(v1,v2);
        float b = glm::distance(v2,v3);
        float c = glm::distance(v1,v3);
        float r = 2*A/(a+b+c);
        float R = a*b*c/(4.0f*A);
        float q_Rr = 2*r/R;
        setRange(_rangeInOutRadius,q_Rr);

        f++;
    }

    for(auto &r:_rangeInOutRadius){
        r.second = 100.0f*r.first/oglWidget->_vcgmesh.fn;
        r.second = QString::number(r.second,'f',5).toFloat();
    }
}

void Window::_computeInRadiusMaxEdge(){
    auto f = oglWidget->_vcgmesh.face.begin();
    while(f!=oglWidget->_vcgmesh.face.end()){
        glm::vec3 v1(f->P(0).X(), f->P(0).Y(), f->P(0).Z());
        glm::vec3 v2(f->P(1).X(), f->P(1).Y(), f->P(1).Z());
        glm::vec3 v3(f->P(2).X(), f->P(2).Y(), f->P(2).Z());

        if(measurementCheckable){
            f->computeTriangleArea();
        }
        float A = f->getArea();
        float a = glm::distance(v1,v2);
        float b = glm::distance(v2,v3);
        float c = glm::distance(v1,v3);
        float r = 2*A/(a+b+c);
        float lenMax = maxValue<float>(a,b,c,true);
        float q_Lr = 2*sqrtf(3)*r/lenMax;
        setRange(_rangeInRadiusMaxEdge,q_Lr);

        f++;
    }

    for(auto &r:_rangeInRadiusMaxEdge){
        r.second = 100.0f*r.first/oglWidget->_vcgmesh.fn;
        r.second = QString::number(r.second,'f',5).toFloat();
    }
}

void Window::_computeMinHeightMaxEdge(){
    auto f = oglWidget->_vcgmesh.face.begin();
    while(f!=oglWidget->_vcgmesh.face.end()){
        glm::vec3 v1(f->P(0).X(), f->P(0).Y(), f->P(0).Z());
        glm::vec3 v2(f->P(1).X(), f->P(1).Y(), f->P(1).Z());
        glm::vec3 v3(f->P(2).X(), f->P(2).Y(), f->P(2).Z());

        if(measurementCheckable){
            f->computeTriangleArea();
        }
        float A = 2.0f*f->getArea();
        float a = glm::distance(v1,v2);
        float b = glm::distance(v2,v3);
        float c = glm::distance(v1,v3);
        float h1 = A/a;
        float h2 = A/b;
        float h3 = A/c;

        float hMin = maxValue<float>(h1,h2,h3);
        float lenMax = maxValue<float>(a,b,c,true);
        float q_Lh = 2.0f*hMin/(sqrtf(3)*lenMax);

        setRange(_rangeMinHeightMaxEdge,q_Lh);

        f++;
    }

    for(auto &r:_rangeMinHeightMaxEdge){
        r.second = 100.0f*r.first/oglWidget->_vcgmesh.fn;
        r.second = QString::number(r.second,'f',5).toFloat();
    }
}

void Window::_computeQ(){
    if(minAngleTbl==nullptr){
        _computeMinAngle();
    }
    if(areaEdgeTbl==nullptr){
        _computeAreaEdge();
    }
    if(inRadiusMaxEdgeTbl==nullptr){
        _computeInRadiusMaxEdge();
    }

    for(int i=0;i<_range;++i){
        _rangeQ[i].second = 0.5f*_rangeAngle[i].second + 2.0f*_rangeAreaEdge[i].second / 6.0f + _rangeInRadiusMaxEdge[i].second / 6.0f;
        _rangeQ[i].second = QString::number(_rangeQ[i].second,'f',5).toFloat();
    }
}

void Window::_computeQ2(){
    auto f = oglWidget->_vcgmesh.face.begin();
    while(f!=oglWidget->_vcgmesh.face.end()){
        glm::vec3 v1(f->P(0).X(), f->P(0).Y(), f->P(0).Z());
        glm::vec3 v2(f->P(1).X(), f->P(1).Y(), f->P(1).Z());
        glm::vec3 v3(f->P(2).X(), f->P(2).Y(), f->P(2).Z());
        auto v1v2 = v2 - v1;
        auto v1v3 = v3 - v1;
        float a1 = _computeVectorAngle(v1v2, v1v3);

        auto v2v1 = v1 - v2;
        auto v2v3 = v3 - v2;
        float a2 = _computeVectorAngle(v2v1, v2v3);

        auto v3v1 = v1 - v3;
        auto v3v2 = v2 - v3;
        float a3 = _computeVectorAngle(v3v1, v3v2);

        float aMin = maxValue<float>(a1,a2,a3);
        float aMax = maxValue<float>(a1,a2,a3,true);
        float Q2 = 1.0f - (aMax-aMin)/(aMin*6.0f);

        setRange(_rangeQ2,Q2);

        f++;
    }
    for(auto &r:_rangeQ2){
        r.second = 100.0f*r.first/oglWidget->_vcgmesh.fn;
        r.second = QString::number(r.second,'f',5).toFloat();
    }
}

float Window::_computeVectorAngle(glm::vec3 &a, glm::vec3 &b){
    a = glm::normalize(a);
    b = glm::normalize(b);
    return glm::degrees(glm::acos(glm::dot(a,b)));
}

void Window::setRange(std::vector<std::pair<int, float> > &container, float val){
    if (val<0.1f) {
        ++container[0].first;
    }
    else if (val >= 0.1f&&val < 0.2f) {
        ++container[1].first;
    }
    else if (val >= 0.2f&&val < 0.3f) {
        ++container[2].first;
    }
    else if (val >= 0.3f&&val < 0.4f) {
        ++container[3].first;
    }
    else if (val >= 0.4f&&val < 0.5f) {
        ++container[4].first;
    }
    else if (val >= 0.5f&&val < 0.6f) {
        ++container[5].first;
    }
    else if (val >= 0.6f&&val < 0.7f) {
        ++container[6].first;
    }
    else if (val >= 0.7f&&val < 0.8f) {
        ++container[7].first;
    }
    else if (val >= 0.8f&&val < 0.9f) {
        ++container[8].first;
    }
    else if (val >= 0.9f&&val <= 1.0f) {
        ++container[9].first;
    }
    else {
        ++container[10].first;
    }

}

//neuron buttons
void Window::on_btnNeuronColor_clicked()
{
    emit mainWindow->render_Mode(NEURON_DIFFERENT_COLOR);
}

void Window::on_btnNeuronGrow1_clicked()
{
    emit mainWindow->render_Mode(GROW_SPINE);
}

void Window::on_btnNeuronGrow2_clicked()
{
    emit mainWindow->render_Mode(GROW_MYELIN_SHEATH);
    if(!neuronSpin2->isEnabled()){
        neuronSpin2->setEnabled(true);
    }
}

void Window::on_btnNeuronSF_clicked()
{
    emit mainWindow->render_Mode(SOMA_FIXED);
    _computeMeasurements();
}

void Window::on_btnNeuronSA_clicked()
{
    emit mainWindow->render_Mode(SOMA_AUTO);
}

//public functions
QString Window::getModelName(){
    return modelBasicGroupBox->title();
}

/*
 *  create widget
 */

QSlider *Window::createSlider()
{
    QSlider *slider = new QSlider(Qt::Vertical);
    slider->setRange(1, 20);
    slider->setSingleStep(1);          //using arrow key
    slider->setPageStep(2);       //using page-up or page-down key
    slider->setTickInterval(1);   //the tickmarks's interval
    slider->setTickPosition(QSlider::TicksRight);   //trickmarks's position
    return slider;
}

QLabel* Window::createLabel(QString c,Qt::Alignment alignment)
{
    QLabel* label = new QLabel(c);
    label->setAlignment(alignment);
    return label;
}

QLineEdit* Window::createLineEdit()
{
    QLineEdit* lineEdit = new QLineEdit();
    lineEdit->setReadOnly(true);
    lineEdit->setPlaceholderText("0.000");
    lineEdit->setFixedWidth(80);
    lineEdit->setAlignment(Qt::AlignCenter);
    return lineEdit;
}

QPushButton* Window::createPushButton(QString c)
{
    QPushButton *pushButton = new QPushButton(c);
    //pushButton->setFixedWidth(50);
    return pushButton;
}

QSpinBox* Window::createSpinBox(int value)
{
    QSpinBox *spinBox = new QSpinBox();
    spinBox->setValue(value);
    return spinBox;
}

QDoubleSpinBox* Window::createDSpinBox(double value)
{
    QDoubleSpinBox *spinBox = new QDoubleSpinBox();
    spinBox->setValue(value);
    spinBox->setRange(0.0,1.0);
    spinBox->setWrapping(true); //0,1,2,...max,0,1,2,...
    spinBox->setSingleStep(0.1);
    spinBox->setEnabled(false);
    return spinBox;
}

QGroupBox* Window::createGroupBox(QString c)
{
    QGroupBox *groupBox = new QGroupBox(c);
    groupBox->setAlignment(Qt::AlignCenter);
    return groupBox;
}
